package com.bw.search.service.impl;

import com.alibaba.fastjson2.JSON;
import com.bw.common.core.utils.StringUtils;
import com.bw.search.common.EsProductModel;
import com.bw.search.service.EsService;
import com.bw.search.vo.SearchParam;
import com.bw.search.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class EsServiceImpl implements EsService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public boolean productStatsUp(String esProductModels) {

        List<EsProductModel> esProductModels1 = JSON.parseArray(esProductModels, EsProductModel.class);

        BulkRequest bulkRequest = new BulkRequest();
        esProductModels1.stream().forEach(esProductModel -> {

            IndexRequest indexRequest = new IndexRequest();
            indexRequest.index("product2");
            indexRequest.id(esProductModel.getSkuId().toString());
            indexRequest.source(JSON.toJSONString(esProductModel), XContentType.JSON);
            bulkRequest.add(indexRequest);

        });

        BulkResponse bulk = null;

        try {
            bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
        return bulk.hasFailures();

    }

    @Override
    public SearchResult search(SearchParam searchParam) throws IOException {

        SearchRequest searchRequest = buildSearchRequest(searchParam);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        SearchResult searchResult = analyerResponse(searchResponse, searchParam);
        return searchResult;
    }

    public SearchResult analyerResponse(SearchResponse searchResponse, SearchParam searchParam){

        SearchResult searchResult = new SearchResult();

        SearchHits hits = searchResponse.getHits();

        List skuEsModes = new ArrayList();
        for (SearchHit hit : hits.getHits()) {
            String sourceAsString = hit.getSourceAsString();
            EsProductModel skuEsModelList = com.alibaba.fastjson.JSON.parseObject(sourceAsString, EsProductModel.class);

            if(StringUtils.isNotEmpty(searchParam.getKeyword())){
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField skuTitle = highlightFields.get("skuTitle");
                skuEsModelList.setSkuTitle(skuTitle.getFragments()[0].toString());
            }

            skuEsModes.add(skuEsModelList);
        }

        searchResult.setProducts(skuEsModes);

        searchResult.setPageNum(searchParam.getPageNum());

        searchResult.setTotal(hits.getTotalHits().value);

        searchResult.setTotalPages(
                Long.valueOf(searchResult.getTotal()%20==0 ? searchResult.getTotal()/20 : (searchResult.getTotal()/20+1)).intValue()
        );

        Aggregations aggregations = searchResponse.getAggregations();

        ParsedLongTerms brandIdAgg = aggregations.get("brandIdAgg");
        List<? extends Terms.Bucket> buckets = brandIdAgg.getBuckets();
        List<SearchResult.BrandVo> brandVos = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            brandVo.setBrandId(Long.valueOf(bucket.getKeyAsString()));

            Terms brandNameAgg = bucket.getAggregations().get("brandNameAgg");
            String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
            brandVo.setBrandName(brandName);

            brandVos.add(brandVo);
        }
        searchResult.setBrands(brandVos);

        List<SearchResult.CatalogVo> catalogs = new ArrayList<>();
        ParsedLongTerms catalogIdAgg = aggregations.get("catalogIdAgg");

        List<? extends Terms.Bucket> catalogBuckets = catalogIdAgg.getBuckets();

        for (Terms.Bucket catalogBucket : catalogBuckets) {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            String catalogId = catalogBucket.getKeyAsString();
            catalogVo.setCatalogId(Long.valueOf(catalogId));
            Terms catalogNameAgg = catalogBucket.getAggregations().get("catalogNameAgg");
            String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogName(catalogName);
            catalogs.add(catalogVo);
        }
        searchResult.setCatalogs(catalogs);


        List<SearchResult.AttrVo> attrs = new ArrayList<>();
        ParsedNested attrsAgg = aggregations.get("attrAgg");
        Terms attrIdAgg = attrsAgg.getAggregations().get("attrIdAgg");

        for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();

            String attrId = bucket.getKeyAsString();
            attrVo.setAttrId(Long.valueOf(attrId));

            Aggregations attrIdBucketAggregations = bucket.getAggregations();

            Terms attrNameAgg = attrIdBucketAggregations.get("attrNameAgg");
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            attrVo.setAttrName(attrName);

            Terms attrValueAgg = attrIdBucketAggregations.get("attrValueAgg");
            List<String> values = new ArrayList<>();
            for (Terms.Bucket attrValueAggBucket : attrValueAgg.getBuckets()) {
                values.add(attrValueAggBucket.getKeyAsString());
            }
            attrVo.setAttrValue(values);

            attrs.add(attrVo);

        }
        searchResult.setAttrs(attrs);

        return searchResult;
    }

    private SearchRequest buildSearchRequest(SearchParam searchParam){

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isNotEmpty(searchParam.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
        }

        if(searchParam.getBrandId() != null && searchParam.getBrandId().size()>0){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
        }

        if(StringUtils.isNotNull(searchParam.getCatalog3Id())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",searchParam.getCatalog3Id()));
        }

        if(StringUtils.isNotEmpty(searchParam.getSkuPrice())){
            RangeQueryBuilder skuPrice = QueryBuilders.rangeQuery("skuPrice");
            String[] split = searchParam.getSkuPrice().split("_");
            if(split.length == 2){
                skuPrice.gte(split[0]);
                skuPrice.lte(split[1]);
            } else if (searchParam.getSkuPrice().endsWith("_")) {
                skuPrice.gte(split[0]);
            }else if (searchParam.getSkuPrice().startsWith("_")){
                skuPrice.lte(split[1]);
            }
            boolQueryBuilder.filter(skuPrice);
        }

        //1.5  nested  attrs   1_3:3&2_4:8
        if (searchParam.getAttrs()!=null && searchParam.getAttrs().size()>0){
            for (String attr : searchParam.getAttrs()){
                String[] split = attr.split("_");
                Long attrId = Long.valueOf(split[0]);
                String[] attrValues = split[1].split(":");
                String attrValue = attrValues[1];
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                boolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValue));
                boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs",boolQuery, ScoreMode.None));
            }
        }

        searchSourceBuilder.query(boolQueryBuilder);

        if(StringUtils.isNotEmpty(searchParam.getSort())){
            String[] split = searchParam.getSort().split("_");
            searchSourceBuilder.sort(split[0],"asc".equalsIgnoreCase(split[1])? SortOrder.ASC:SortOrder.DESC);
        }

        searchSourceBuilder.from(0);
        searchSourceBuilder.size(20);

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("skuTitle");
        highlightBuilder.preTags("<span style='color:skyblue'>");
        highlightBuilder.postTags("</span>");

        searchSourceBuilder.highlighter(highlightBuilder);

        TermsAggregationBuilder brandId = AggregationBuilders.terms("brandIdAgg").field("brandId");
        TermsAggregationBuilder brandName = AggregationBuilders.terms("brandNameAgg").field("brandName");
        TermsAggregationBuilder brandImg = AggregationBuilders.terms("brandImgAgg").field("brandImg");
        brandId.subAggregation(brandName);
        brandId.subAggregation(brandImg);

        TermsAggregationBuilder catalogId = AggregationBuilders.terms("catalogIdAgg").field("catalogId");
        TermsAggregationBuilder catalogName = AggregationBuilders.terms("catalogNameAgg").field("catalogName");
        catalogId.subAggregation(catalogName);

        NestedAggregationBuilder attrs = AggregationBuilders.nested("attrAgg", "attrs");
        TermsAggregationBuilder attrId = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId");
        TermsAggregationBuilder attrName = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName");
        TermsAggregationBuilder attrValue = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue");

        attrs.subAggregation(attrId);
        attrId.subAggregation(attrName);
        attrId.subAggregation(attrValue);

        searchSourceBuilder.aggregation(brandId);
        searchSourceBuilder.aggregation(catalogId);
        searchSourceBuilder.aggregation(attrs);

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("product");
        searchRequest.source(searchSourceBuilder);
        return searchRequest;

    }



}
